package org.fujene.memoryblock;

import java.io.IOException;
import java.util.Arrays;

import org.fujene.structure.Constants;

public abstract class MemoryBlock {
	private int ID;
	private int type;
	protected int headerSize;
	protected int bodySize;
	private byte[] body;
	protected int bodyVacancy;
	private boolean isAboutToFlush;
	
	protected static final int PTR = Constants.BYTE_GLOBALPTR;
	protected static final int RELPTR = Constants.BYTE_RELATIVEPTR;
	protected static final int STRLEN = Constants.BYTE_STRLEN;
	protected static final int DOCFREQ = Constants.BYTE_DOCFREQ;
	protected static final int TERMFREQ = Constants.BYTE_TERMFREQ;

	public MemoryBlock(int bodySize, int ID, int type, boolean isAllocate) {
		this.ID = ID;
		this.bodySize = bodySize;
		this.type = type;
		if (isAllocate) {
			this.body = new byte[bodySize];
			bodyVacancy = bodySize;
		}
		this.isAboutToFlush = false;
	}

	public MemoryBlock(int bodySize, int ID, boolean isAllocate) {
		this(bodySize, ID, Constants.MEMORY_TYPE_DICT, isAllocate);
	}

	public MemoryBlock(int bodySize, int ID) {
		this(bodySize, ID, true);
	}

	public int getID() {
		return ID;
	}
	
	public int getBodySize(){
		return this.bodySize;
	}
	
	public abstract int getOptionSize();
	public abstract byte[] getOptionByteStream();
	public abstract void setOption(int option, int value);
	public abstract byte[] getData() throws IOException;
	public abstract void setData(byte[] array) throws IOException;
	
	// 読み出し TODO: 読み込みロックの必要性
	public byte[] getBody(int from, int length) {
		if (from + length > bodySize)
			return null;
		else if(length < 0)
			return null;

		return Arrays.copyOfRange(body, from, from + length);
	}

	// 移動
	protected void moveBody(int from, int length, int offset) {
		if (offset == 0)
			return;
		synchronized (body) {
			if (offset > 0) {
				// 後ろから移動する
				for (int i = from + length - 1; i >= from; i--) {
					body[i + offset] = body[i];
				}
			} else {
				// 前から移動する
				for (int i = from; i < from + length - 1; i++) {
					body[i + offset] = body[i];
				}
			}
		}
	}

	public int getType() {
		return type;
	}

	public void setBody(byte[] values, int from) {
		if (from + values.length > bodySize)
			return;
		
		synchronized (body) {
			for (int i = 0; i < values.length; i++)
				body[from + i] = values[i];
		}
	}
	
	public void setBody(byte[] values, int vfrom, int vlen, int bfrom){
		if (bfrom + vlen > bodySize)
			return;
		
		synchronized (body) {
			for (int i = 0; i < vlen; i++)
				body[bfrom + i] = values[vfrom + i];
		}
	}

	public void setType(int type) {
		this.type = type;
	}
	
	public int getUsed(){
		return this.bodySize - this.bodyVacancy;
	}
	
	public int getBodyVacancy() {
		return bodyVacancy;
	}
	
	public void setBodyVacancy(int bodyVacancy) {
		this.bodyVacancy = bodyVacancy;
	}
	
	public void setAboutToFlush(boolean isAboutToFlush) {
		this.isAboutToFlush = isAboutToFlush;
	}
	
	public boolean isAboutToFlush() {
		return isAboutToFlush;
	}

	public void setHash() {
		// TODO: ハッシュの決め方 CRC32？
		// blockCRC = header.hashCode();
		// blockCRC += body.hashCode();
	}
	
	public abstract String getClassName();
}